package away3d.animators.skeleton  ;


/**
 * Author: David Lenaerts
 */

import flash.geom.Vector3D ;



/**
 * SkeletonTimelineClipNode represents a node in a skeleton tree containing a blending clip for which the keyframes
 * are laid out on a timeline. As such, the pose is determined by the time property.
 */
class SkeletonTimelineClipNode extends SkeletonClipNodeBase
{
    
    
    
    private var _timeDir        : Float ;
    private var _frame1         : Int ;
    private var _frame2         : Int ;
    private var _blendWeight    : Float = 0 ;
    private var _framesInvalid  : Bool ;
    private var _resetTime      : Boolean ;
    private var _startTime      : Float = 0 ;
    private var _lastFrame      : Int ;
    
    
    /**
     * Creates a new SkeletonTimelineClipNode object.
     * @param numJoints The amount of joints in the target skeleton.
     */
    public function new()
    {
        
        super() ;
        _blendWeight    = 0 ;
        _startTime      = 0 ;
        
    }
    
    
    public function reset()
    {
        
        if( !clip.looping )
        {
            
            _resetTime = true ;
            
        }
        
    }
    
    
    override public var time( get_time, set_time ) : Float ;
    
    
    override private function set_time( value : Float ) : Float
    {
        
        if( _resetTime )
        {
            
            _startTime = value ;
            _resetTime = false ;
            
        }
        
        if( _time == value ) return ;
        
        _framesInvalid  = true ;
        _timeDir        = value - _time ;
        super.time      = value ;
        
    }
    
    
    /**
     * @inheritDoc
     */
    override public function updatePose( skeleton : Skeleton )
    {
        
        if( _clip.duration == 0 ) return ;
        if( _framesInvalid ) updateFrames( _time - _startTime ) ;
        
        var poses1                          = clip._frames[ _frame1 ].jointPoses ;
        var poses2                          = clip._frames[ _frame2 ].jointPoses ;
        var numJoints                       = skeleton.numJoints ;
        var p1          : Vector3D<Float> ; 
        var p2          : Vector3D<Float> ;
        var pose1       : JointPose ;
        var pose2       : JointPose ;
        var endPoses                        = skeletonPose.jointPoses ;
        var endPose     : JointPose ;
        var tr          : Vector3D<Float> ;
        
        // :s
        if( endPoses.length != numJoints ) endPoses.length = numJoints ;
        
        if( ( numJoints != poses1.length ) || ( numJoints != poses2.length ) )
        {
            
            throw new Error( "joint counts don't match!" ) ;
            
        }
        
        for( i in 0 in numJoints )
        {
            
            endPose = endPoses[ i ] ||= new JointPose() ;
            if( endPos == null )
            {
                
                endPos = new JointPose() ;
                endPoses[ i ] = endPos;
                
            }
            
            pose1   = poses1[ i ] ;
            pose2   = poses2[ i ] ;
            p1      = pose1.translation ;
            p2      = pose2.translation ;

            if( _highQuality )
            {
                
                endPose.orientation.slerp( pose1.orientation, pose2.orientation, _blendWeight ) ;
                
            }
            else
            {
                
                endPose.orientation.lerp( pose1.orientation, pose2.orientation, _blendWeight ) ;
                
            }
            
            if( i > 0 ) 
            {
                
                tr      = endPose.translation ;
                tr.x    = p1.x + _blendWeight*( p2.x - p1.x ) ;
                tr.y    = p1.y + _blendWeight*( p2.y - p1.y ) ;
                tr.z    = p1.z + _blendWeight*( p2.z - p1.z ) ;
            }
            
        }
        
    }
    
    
    /**
     * @inheritDoc
     */
    override public function updatePositionData()
    {
        
        if( _framesInvalid ) updateFrames( _time-_startTime ) ;
        
        var p1  = clip._frames[ _frame1 ].jointPoses[ 0 ].translation ;
        var p2  = clip._frames[ _frame2 ].jointPoses[ 0 ].translation ;

        if ( ( _timeDir > 0 && _frame2 > _lastFrame ) || ( _timeDir < 0 && _frame1 < _lastFrame ) )
        {
            
            var dx          = _rootPos.x ;
            var dy          = _rootPos.y ;
            var dz          = _rootPos.z ;
            _rootPos.x      = p1.x + _blendWeight*( p2.x - p1.x ) ;
            _rootPos.y      = p1.y + _blendWeight*( p2.y - p1.y ) ;
            _rootPos.z      = p1.z + _blendWeight*( p2.z - p1.z ) ;
            rootDelta.x     = _rootPos.x - dx ;
            rootDelta.y     = _rootPos.y - dy ;
            rootDelta.z     = _rootPos.z - dz ;
        
        }
        else // jumping back, need to reset position
        {
            
            _rootPos.x      = p1.x + _blendWeight*( p2.x - p1.x ) ;
            _rootPos.y      = p1.y + _blendWeight*( p2.y - p1.y ) ;
            _rootPos.z      = p1.z + _blendWeight*( p2.z - p1.z ) ;
            rootDelta.x     = 0 ;
            rootDelta.y     = 0 ;
            rootDelta.z     = 0 ;
            
        }
        
        _lastFrame = _timeDir > 0? _frame1 : _frame2 ;
        
    }
    
    
    private function updateFrames( time : Float )
    {
        
        var lastFrame   : Int ;
        var dur         : Int, 
        var frameTime   : Int ;
        var frames              = _clip._frames ;
        var durations           = _clip._durations ;
        var duration            = _clip._totalDuration ;
        var looping             = _clip.looping ;
        var numFrames           = frames.length ;
        
        if ( numFrames == 0 ) return ;
        
        lastFrame = numFrames - 1 ;
        
        if( looping )
        {
            
            if( time >= 1 ) time -= int( time ) ;
            if( time < 0 )  time -= int( time )-1 ;
            
            if( _clip._fixedFrameRate )
            {
                
                time            *= lastFrame ;
                _frame1         = time ;
                _blendWeight    = time - _frame1 ;
                _frame2         = _frame1 + 1 ;
                
            }
            else 
            {
                do
                {
                    
                    frameTime   = dur ;
                    dur         += durations[ _frame1 ] ;
                    _frame1     = _frame2 ;
                    
                    if ( ++_frame2 == numFrames ) 
                    {
                        
                        _frame1 = 0 ;
                        _frame2 = 1 ;
                        
                    }
                    
                } 
                while ( time > dur ) ;

                _blendWeight = ( time - frameTime ) / durations[ _frame1 ] ;
                
            }
            
        }
        else
        {
            
            if( time >= 1 )
            {
                
                clip.notifyPlaybackComplete() ;
                //if the animation has different frame count, set the frame to the lastframe return an error
                //_frame1 = lastFrame ;
                //_frame2 = lastFrame ;
                _frame1         = 0 ;
                _frame2         = 0 ;
                _blendWeight    = 0 ;
                
            }
            else if( _clip._fixedFrameRate )
            {
                
                time            *= lastFrame ;
                _frame1         = time ;
                _frame2         = _frame1 + 1 ;
                
                if ( _frame2 == numFrames ) _frame2 = _frame1 ;
                
                _blendWeight    = time - _frame1 ;
                
            }
            else
            {
                
                time *= duration - durations[ lastFrame ] ;
                
                do
                {
                    
                    frameTime   = dur ;
                    dur         += durations[ _frame1 ] ;
                    _frame1     = _frame2++ ;
                    
                } 
                while ( time > dur ) ;

                if ( _frame2 == numFrames )
                {
                    
                    _frame2         = _frame1 ;
                    _blendWeight    = 0 ;
                    
                }
                else
                {
                    
                    _blendWeight = ( time - frameTime ) / durations[ _frame1 ] ;
                }
                
            }
            
        }
        
        _framesInvalid = false ;
        
    }
    
    
}
